/*
 * ruletangle.cpp
 *
 *  Created on: Jun 25, 2011
 *      Author: Mo
 */

#include "ruletangle2.h"
#include <assert.h>
#include "freefuncs.h"
#include "EaToFindRectangles.h"


Ruletangle2::Ruletangle2 (void)
{}

Ruletangle2::Ruletangle2(const Ruletangle2& other)
{
	this->majorLongInstance = other.majorLongInstance;
	//this->minorLongInstance = other.minorLongInstance;
	//for (int i = 0; i < (int) other.majorRectangle2.size(); i++)
	//	this->majorRectangle2.push_back(other.majorRectangle2[i]);
	for (int i = 0; i < (int) other.minorRectangle2.size(); i++)
		this->minorRectangle2.push_back(other.minorRectangle2[i]);
}

Ruletangle2::Ruletangle2(const LongInstance& l)
{
	assert (l.getConflictNum() == 0 && "Error: Creating a ruletangle with only one [conflict-free] rule but the number of conflicting coordinates is not zero.");
#ifdef COMMENTS_ON
	cout << "Creating a Ruletangle2 object from a single no conflicting RuleBase object.\n";
#endif
	this->majorLongInstance = l;
}

//TODO: add the types. refer to the header files for the different types. the default type is set to 1 here and we don't use it yet.
Ruletangle2::Ruletangle2(const LongInstance& l1, const LongInstance& l2, Rectangle2FormationAlgorithmType algorithm, int type)
{
#ifdef COMMENTS_ON
	cout << "Creating a Ruletangle2 object from a pair of conflicting RuleBase objects.\n";
#endif
	switch (algorithm)
	{
	case topdown:
		constructFromConflictingLIs_topdown(l1, l2, type);
		break;
	case bottomup:
		constructFromConflictingLIs_bottomup(l1, l2, type);
		break;
	case evolution:
		constructFromConflictingLIs_evolution(l1, l2, type);
		break;
	default:
		cerr << "\nInvalid Algorithm type for building ruletangle2";
		assert (false && "Invalid Algorithm type for building ruletangle2");
	}
}

Ruletangle2::~Ruletangle2()
{}

bool Ruletangle2::applies (const vector<Neighborhood*>& nbr) const //to do after compression of if part
{
	assert( (int)nbr.size() == this->majorLongInstance.neighborhoodNum()
			&& "\nError: the given IF part has not the same lenghts as of the LongInstance's IF part.");

	if (this->majorLongInstance.hasSameFeature(nbr))
		return true;
	else
		return false;
}

int Ruletangle2::label (const vector<Neighborhood*>& nbr, const Coordinate& coo) const
{
	assert (this->majorLongInstance.getOccuranceNum() > 0);
	if (!this->applies(nbr)) return -1;

	for (int i = 0; i < (int) this->minorRectangle2.size(); i++)
		if (minorRectangle2[i].cooIsInside(coo))
		{
			//return WHITE;
			if (this->majorLongInstance.getLabel() == WHITE)
				return BLACK;
			else
				return WHITE;
		}
	
		
	return this->majorLongInstance.getLabel();

	//return BLACK;
}

void Ruletangle2::print(void) const
{
	cout << endl << "This Ruletangle2 object has:" << endl;
	cout << "LongInstance: " ;
	this->majorLongInstance.print();
	cout << "Number of Minor Rectangle2s: " << this->minorRectangle2.size() << endl;
}

void Ruletangle2::printDetail(void) const
{
	cout << endl << "This Ruletangle2 object has:" << endl;
	cout << "LongInstance: " ;
	this->majorLongInstance.print();
	cout << "Number of Minor Rectangle2s: " << this->minorRectangle2.size() << endl;
	cout << "Coordinates in the long instance: ";
	for (int i = 0; i < (int) this->majorLongInstance.getCooNum(); i++)
	{
		cout << i << "-";
		this->majorLongInstance.getCoordinate(i).print();
		cout << " ";
	}
	cout << endl << "Minor Rectangle2s: " << endl;
	for (int i = 0; i < (int) this->minorRectangle2.size(); i++)
	{
		cout << i << "- ";
		this->minorRectangle2[i].print();
		cout << endl;
	}
	cout << endl;
}

ostream& operator<< (ostream& stream, const Ruletangle2& rt)
{
	stream << rt.majorLongInstance;
	if (rt.minorRectangle2.size() >0)
	{
		stream << "Minor Rectangle2s (" << rt.minorRectangle2.size() << " objects):" << endl;
		for (int i = 0; i < (int) rt.minorRectangle2.size(); i++)
		{
			stream << rt.minorRectangle2[i] << endl;
		}
	}
	return stream;
}

bool Ruletangle2::noMajorInside (const Rectangle2& rectangle2)
{
	for (int i = 0; i < this->majorLongInstance.getCooNum(); i++)
		if (rectangle2.cooIsInside(this->majorLongInstance.getCoordinate(i)))
			return false;
	return true;
}

bool Ruletangle2::noMajorInside (const Rectangle& Rectangle1)
{
	for (int i = 0; i < this->majorLongInstance.getCooNum(); i++)
		if (Rectangle1.cooIsInside(this->majorLongInstance.getCoordinate(i)))
			return false;
	return true;
}

int Ruletangle2::majorsInside (const Rectangle2& Rectangle2)
{
	int result = 0;
	for (int i = 0; i < this->majorLongInstance.getCooNum(); i++)
		if (Rectangle2.cooIsInside(this->majorLongInstance.getCoordinate(i)))
			result++;
	return result;
}

int Ruletangle2::majorsInside (const Rectangle& Rectangle1, bool reportOnInside)
{
	int result = 0;
	for (int i = 0; i < this->majorLongInstance.getCooNum(); i++)
		if (Rectangle1.cooIsInside(this->majorLongInstance.getCoordinate(i)))
		{
			result++;
			if (reportOnInside)
				cerr << "Error: Coo " << this->majorLongInstance.getCoordinate(i) << " is inside the Rectangle " << Rectangle1;
		}
	return result;
}

//void Ruletangle2::divideMinErrors (const Rectangle& source, Rectangle& t1, Rectangle& t2)
//{
//	//assert(this->noMajorInside(source) && "The given Rectangle2 is already error free. No conflict to be resolved.");
//	int totalCoo = source.gettotalCoo();
//	cout << "\ndivideMinErrors() called with " << totalCoo << " coos.";
//	if (totalCoo >= 32) //It can go up to 64 but the computation time takes forever. I set it to 32 for getting some results for now.
//	{
//		assert("Too many coordinates in the Rectangle2, out of computing power.");
//		this->majorLongInstance.clear();
//		this->minorRectangle2.clear();
//		return;
//	}
//	unsigned __int64 bestDivisionIndex = 0;
//	Rectangle best1, best2;
//	int minErrors = totalCoo + 1;
//	bool* label = new bool[totalCoo];
//	int totalMajorsInsideEither;
//	unsigned __int64 totalCombinations = twoToThePowerOf(totalCoo);
//	for (unsigned __int64 i = 1; i < totalCombinations; i++)
//	{
//		//cout << "Doing " << totalCombinations << " combinations. ";
//		if (i % 1000000 == 0)
//			cout << "  " << i/1000000 << "/" << totalCombinations/1000000 << "...";
//		best1.clearCoos();
//		best2.clearCoos();
//		//RuletangleHelperFuncs::binary(i, totalCoo, label);
//		RuletangleHelperFuncs::longBinary(i, totalCoo, label);
//		for (int j = 0; j < totalCoo; j++)
//			if (label[j] == 0)
//				best1.addCooNoCheck(source.getCoo(j));
//			else
//				best2.addCooNoCheck(source.getCoo(j));
//		
//		totalMajorsInsideEither = this->majorsInside(best1) + this->majorsInside(best2);
//		if (totalMajorsInsideEither < minErrors)
//		{
//			minErrors = totalMajorsInsideEither;
//			bestDivisionIndex = i;
//		}
//		if (minErrors == 0)
//			break;
//	}
//	//cout << endl;
//	longBinary (bestDivisionIndex, totalCoo, label);
//	for (int j = 0; j < totalCoo; j++)
//			if (label[j] == 0)
//				t1.addCooNoCheck(source.getCoo(j));
//			else
//				t2.addCooNoCheck(source.getCoo(j));
//	delete[] label;
//	//TODO: keep the best rectangles somewhere and use opertor= for t1 and t2 here to speed up.
//}

int Ruletangle2::getNetInfoSizeInBits(void) const
{
	/*
	  The effective information in an object:
	  one if-then rule and n rectangles (minor rectangles).
	  the rule size is "majorLongInstance.getSize()" number of bits for the if part and one bit for the "then" part.
	  the rectangles need n * 2 (2 coordinate is stored per rectangle) * 2*8 (number of bits per coo) bits.
	  */
	int majorLISize = (this->majorLongInstance.getFeatureSize() + 1) * BITS_PER_NBR;
	int minorRectSize = this->minorRectangle2.size()*2*2*BITS_PER_ROWCOL;
	return (majorLISize + minorRectSize);
}

bool Ruletangle2::hasCooAsMinor(const Coordinate& coo) const
{
	int rectNum = this->minorRectangle2.size();
	if ( rectNum == 0)
		return false;
	for (int i = 0; i < rectNum; i++)
		if (minorRectangle2[i].cooIsInside(coo))
			return true;

	return false;
}

void Ruletangle2::constructFromConflictingLIs_topdown(const LongInstance& l1, const LongInstance& l2, int type)
{
	int majorSize, minorSize;
	LongInstance majorLI, minorLI;

	detectMajorAndMinorMembers(l1, l2, majorLI, minorLI, majorSize, minorSize, type);

	this->majorLongInstance = majorLI;
	this->minorRectangle2.clear();
	if (minorSize == 0)
		return;
	if (minorSize == 1)
	{
		this->minorRectangle2.push_back(Rectangle2(minorLI.getCoordinate(0)));
		return;
	}

	if (this->noMajorInside(minorLI)) //if the current minor rectangle (that has all the minor coos) 
	{
		this->minorRectangle2.push_back(Rectangle2(minorLI));
		return;
	}
	
	//divideMinErrors() works on Rectangle objects and not Rectangle2 because we need to know the coordinates in each rectangle.
	//we then need to use Rectangle objects here.
	vector<Rectangle> raw, complete;
	Rectangle currentRect1(&minorLI), halfRect1, halfRect2;
	raw.push_back(currentRect1); //initiating raw. needs to have at least one object.
	while (raw.size() > 0)
	{
		currentRect1 = raw.back();
		raw.pop_back();
		if (this->noMajorInside(currentRect1))
			complete.push_back(currentRect1);
		else
		{
			bool successfulDivision = currentRect1.divideToTwoWithMinCooErrors(majorLongInstance.getCoordinateVector(), halfRect1, halfRect2);
			//this->divideMinErrors(currentRect1, halfRect1, halfRect2);
			if (successfulDivision)
			{
				this->majorLongInstance.clear();
				this->minorRectangle2.clear();
				this->majorLongInstance.setLabel(-1);
				return;
			}
			raw.push_back(halfRect1);
			raw.push_back(halfRect2);
		}
	}

	for (int i = 0; i < (int) complete.size(); i++)
		this->minorRectangle2.push_back(complete[i]);
}

void Ruletangle2::constructFromConflictingLIs_bottomup(const LongInstance& l1, const LongInstance& l2, int type)
{
	int majorSize, minorSize;
	LongInstance majorLI, minorLI;

	detectMajorAndMinorMembers(l1, l2, majorLI, minorLI, majorSize, minorSize, type);

	this->majorLongInstance = majorLI;
	int bestRectIndex;
	for (int i = 0; i < minorLI.getCooNum(); ++i)
	{
		Coordinate toBeAdded = minorLI.getCoordinate(i);
		bestRectIndex = minRectIndexWithMinExpansion (majorLI, toBeAdded);
		if (bestRectIndex == -1)
			this->minorRectangle2.push_back(Rectangle2(toBeAdded));
		else
			minorRectangle2[bestRectIndex].expand(toBeAdded);
	}

}

void Ruletangle2::constructFromConflictingLIs_evolution(const LongInstance& l1, const LongInstance& l2, int type)
{
	int majorSize, minorSize;
	LongInstance majorLI, minorLI;

	detectMajorAndMinorMembers(l1, l2, majorLI, minorLI, majorSize, minorSize, type);
	this->majorLongInstance = majorLI;

	if ((int)minorLI.getCooNum() == 1)
	{
		this->minorRectangle2.push_back(Rectangle2(minorLI.getCoordinate(0)));
		return;
	}

	EaToFindRectangles EA (majorLI, minorLI);
	EA.setParameters (200,1000, 200, 3, 100, 2); //popsize, generations, parentSelection, parent pool size, elites
	vector<Rectangle> rct1vect = EA.runAndReturnResult();
	//int majorsInMinorRectangles = 0;
	vector<Rectangle> errorFreeRectVec;
	errorFreeRectVec.reserve(rct1vect.size());

	const int maxAcceptableMajors = 10;
	for (vector<Rectangle>::iterator recIT =rct1vect.begin(); recIT != rct1vect.end(); ++recIT)
	{
		vector<Coordinate> troubleMakingCoos = recIT->getThoseCoosInsideOnly (majorLongInstance.getCoordinateVector());
		if ((int)troubleMakingCoos.size() == 0)
		{
			errorFreeRectVec.push_back(*recIT);
		}

		bool resolved = ((int)troubleMakingCoos.size() > 0);
		if ((int)troubleMakingCoos.size() == 1)
		{
			//let' see if we can fix it!
			//First divide the faulties in 2 if possible
			Rectangle halfRect1, halfRect2;
			resolved = recIT->divideToTwoNotCoveringcoo(*troubleMakingCoos.begin(), halfRect1, halfRect2);
			if (resolved)
			{
				if (halfRect1.gettotalCoo() > 0)
					errorFreeRectVec.push_back(halfRect1);
				if (halfRect2.gettotalCoo() > 0)
					errorFreeRectVec.push_back(halfRect2);
				cout << "o";
			}
		}
		if (!resolved && (int)troubleMakingCoos.size() > 0 && recIT->gettotalCoo() < maxAcceptableMajors)
		{
			vector<Rectangle> subRects = recIT->breakRectangleToExcludeCoos(majorLongInstance.getCoordinateVector());
			errorFreeRectVec.insert( errorFreeRectVec.end(), subRects.begin(), subRects.end() );
			resolved = true;
			cout << "x";
		}
		else if (!resolved && (int)troubleMakingCoos.size() > 0)
		{
			std::cerr << endl << "EA failed to find the good set of minor rectangles. Errors: " << troubleMakingCoos.size() << endl;
			majorLongInstance.clear();
			errorFreeRectVec.clear();
			//this->minorRectangle2.clear(); it's already empty
			return;
		}

	}

	for (int i = 0; i < (int) errorFreeRectVec.size(); ++i)
	{
		this->minorRectangle2.push_back(Rectangle2(errorFreeRectVec[i]));
	}
}

int Ruletangle2::minRectIndexWithMinExpansion (const LongInstance& majorLI, const Coordinate& toBeAdded) const
{
	int minorRectNum = this->minorRectangle2.size();
	if (minorRectNum == 0)
		return -1;
	//int* expansionSize = new int[minorRectNum];
	float* expansionSize = new float[minorRectNum];
	bool canExpand;
	Rectangle2 imaginaryRect;
	for (int i = 0; i < minorRectNum; ++i)
	{
		canExpand = true;
		imaginaryRect = minorRectangle2[i].virtualExpand(toBeAdded);
		for (int cooIndex = 0; cooIndex < majorLI.getCooNum() && canExpand; ++cooIndex)
		{
			if (imaginaryRect.cooIsInside(majorLI.getCoordinate(cooIndex)))
				canExpand = false;
		}
		if (!canExpand)
			expansionSize[i] = -1;
		else 
			//expansionSize[i] = minorRectangle2[i].minDistance(toBeAdded);
			expansionSize[i] = minorRectangle2[i].minDistance_euqleadian(toBeAdded);
	}

	int bestIndex = 0;
	while (expansionSize[bestIndex] == -1 && bestIndex < minorRectNum)
		bestIndex++;

	//if all the distances are -1 (no rectangle2 can be expanded), return -1
	if (bestIndex == minorRectNum)
	{
		delete[] expansionSize;
		return -1;
	}

	//finding the index of closest rectngle2
	for (int i = bestIndex + 1; i < minorRectNum; ++i)
		if ( expansionSize[i] != -1 && expansionSize[i] < expansionSize[bestIndex])
		{
			bestIndex = i;
		}

	delete[] expansionSize;
	return bestIndex;

}

void Ruletangle2::detectMajorAndMinorMembers(const LongInstance& l1, const LongInstance& l2, LongInstance& majorLI, LongInstance& minorLI, 
	int& majorSize, int& minorSize, int type)
{
	//bool l1IsDominant;
	assert (l1.getLabel() != l2.getLabel() && l1.hasSameFeature(l2) &&
		"rules l1 and l2 should have the same IF part and different THEN part");
	switch (type)
	{
	case 1:
	if (l1.getLabel() == BLACK)
	{
		//l1IsDominant = true;
		majorSize = l1.getOccuranceNum();
		minorSize = l2.getOccuranceNum();
		majorLI = l1;
		minorLI = l2;
	}
	else
	{
		//l1IsDominant = false;
		majorSize = l2.getOccuranceNum();
		minorSize = l1.getOccuranceNum();
		majorLI = l2;
		minorLI = l1;
	}
	break;
	}
}